package com.xq.jltx.kotlinandroid.mmkv

import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import android.os.SystemClock
import android.text.TextUtils
import android.util.Log
import androidx.core.content.SharedPreferencesCompat
import com.google.gson.Gson
import com.tencent.mmkv.MMKV
import java.io.*
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.lang.reflect.Type
import kotlin.reflect.KProperty

/**
 * @author aidenquan
 *         Created by junlintianxia on 2021/06/30.
 */
abstract class MMKVDelegate<T>(
    private val key: String?,
    private val defValue: T,
    private val type: Type? = null,
    isMultiProcess: Boolean = false,
    cryptKey: String? = null
) {

    protected abstract val application: Application

    /**a
     * SharedPreferences file name. `default_preferences.xml`.
     */
    protected open val name: String = "default_preferences"

    private val mmkv by lazy {
        val xmlName = "${if (name.isNullOrEmpty()) application.packageName else name}_kv"
        if (MMKV.getRootDir().isNullOrEmpty()) {
            MMKV.initialize(application)
        }
        val mode = if (isMultiProcess) {
            MMKV.MULTI_PROCESS_MODE
        } else {
            MMKV.SINGLE_PROCESS_MODE
        }
        MMKV.mmkvWithID(xmlName, mode, cryptKey)
    }

    private val gson by lazy {
        Gson()
    }


    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return getSPCore(getKey(key, property), defValue, type)
    }


    @SuppressLint("CommitPrefEdits")
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
        setSPCore(getKey(key, property), value)
    }

    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
    @JvmOverloads
    fun <T> getSPCore(key: String, defValue: T, type: Type? = null): T {
        val lastTime = SystemClock.elapsedRealtime()
        val value =  with(mmkv) {
            when (defValue) {
                is String -> getString(key, defValue)
                is Int -> getInt(key, defValue)
                is Long -> getLong(key, defValue)
                is Float -> getFloat(key, defValue)
                is Boolean -> getBoolean(key, defValue)
                else -> {
                    val str = getString(key, "");
                    if (TextUtils.isEmpty(str) || type == null) {
                        defValue
                    } else {
                        gson.fromJson(str, type)
                    }
                }
            }
        } as T
        Log.d(
            "HHHHHH",
            "getSPCore name:${name}, key:${key}, timer:${SystemClock.elapsedRealtime() - lastTime}, value:${value}"
        )
        return value
    }

    @JvmOverloads
    fun setSPCore(key: String, value: Any?, isSync: Boolean = false) {
        val lastTime = SystemClock.elapsedRealtime()
        try {
            with(mmkv.edit()) {
                when (value) {
                    null -> remove(key)
                    is String -> putString(key, value)
                    is Int -> putInt(key, value)
                    is Long -> putLong(key, value)
                    is Float -> putFloat(key, value)
                    is Boolean -> putBoolean(key, value)
                    else -> {
                        putString(key, gson.toJson(value))
                    }
                }

            }
        } catch (e: Exception) {
        }
        Log.d(
            "HHHHHH",
            "setSPCore name:${name}, key:${key}, timer:${SystemClock.elapsedRealtime() - lastTime}, value:${value}"
        )
    }

    fun clearSPCore() {
        mmkv.clear()
    }

    fun clearSPCore(key: String) {
        mmkv.remove(key)
    }

    fun containsCore(key: String): Boolean {
        return mmkv.contains(key)
    }

    fun getAllCore(): MutableMap<String, *> {
        return mmkv.all
    }
}